﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using DAWSharp.Common.Helpers;

namespace DAWSharp.FileFormats.WavFile
{
    public class WavFile
    {
        Entities.FileHeader _fileHeader;
        Entities.FormatHeader _formatHeader;
        Entities.DataHeader _dataHeader;

        public long numberOfSamples { get; set; }
        bool _validated { get; set; }

        float[] _sampleData;

        public WavFile()
        {
            numberOfSamples = 0;
            _validated = false;
        }

        public void OpenWavFile(string filePath)
        {
            using (BinaryReader binaryData = new BinaryReader(File.Open(filePath, FileMode.Open)))
            {
                //read FILE chunk
                _fileHeader = new Entities.FileHeader()
                {
                    fileChunkID = binaryData.ReadChars(4),
                    fileChunkSize = binaryData.ReadInt32(),
                    format = binaryData.ReadChars(4)
                };

                if (!_fileHeader.IsValid())
                {
                    _validated = false;
                    return;
                }

                //read FORMAT chunk
                _formatHeader = new Entities.FormatHeader()
                {
                    formatChunkID = binaryData.ReadChars(4),
                    formatChunkSize = binaryData.ReadInt32(),
                    audioFormat = binaryData.ReadInt16(),
                    numberOfChannels = binaryData.ReadInt16(),
                    sampleRate = binaryData.ReadInt32(),
                    byteRate = binaryData.ReadInt32(),
                    blockAlign = binaryData.ReadInt16(),
                    bitsPerSample = binaryData.ReadInt16(),
                };

                if (!_formatHeader.IsValid())
                {
                    _validated = false;
                    return;
                }

                //read DATA chunk
                _dataHeader = new Entities.DataHeader()
                {
                    dataChunkID = binaryData.ReadChars(4),
                    dataChunkSize = binaryData.ReadInt32()
                };

                if (!_dataHeader.IsValid())
                {
                    _validated = false;
                    return;
                }

                //Read the audio data
                int length = (_dataHeader.dataChunkSize / _formatHeader.blockAlign * _formatHeader.numberOfChannels);

                numberOfSamples = length;
                _sampleData = new float[length];

                switch (_formatHeader.bitsPerSample)
                {
                    case 16:
                        {
                            for (int i = 0; i < length; i++)
                            {
                                _sampleData[i] = ConversionHelper.SixteenBitToFloat(binaryData.ReadInt16());
                            }
                        } break;
                    case 24:
                        {
                            for (int i = 0; i < length; i++)
                            {
                                _sampleData[i] = ConversionHelper.TwentyFourBitToFloat(binaryData.ReadBytes(3));
                            }
                        } break;
                    case 32:
                        {
                            for (int i = 0; i < length; i++)
                            {
                                _sampleData[i] = ConversionHelper.ThirtyTwoBitToFloat(binaryData.ReadInt32());
                            }
                        } break;
                    default:
                        {
                            _validated = false;
                            return;
                        }
                }

                _validated = true;
                return;
            }
        }

        public void SaveWavFile(string filePath)
        {
            using (BinaryWriter writer = new BinaryWriter(File.Open(filePath, FileMode.Create)))
            {
                //Write File Header              
                if (_fileHeader != null)
                {
                    writer.Write(_fileHeader.fileChunkID);
                    writer.Write(_fileHeader.fileChunkSize);
                    writer.Write(_fileHeader.format);
                }
                else
                {
                    Console.WriteLine("No File Header to write. Aborting");
                    return;
                }

                //Write Format Header              
                if (_formatHeader != null)
                {
                    writer.Write(_formatHeader.formatChunkID);
                    writer.Write(_formatHeader.formatChunkSize);
                    writer.Write(_formatHeader.audioFormat);
                    writer.Write(_formatHeader.numberOfChannels);
                    writer.Write(_formatHeader.sampleRate);
                    writer.Write(_formatHeader.byteRate);
                    writer.Write(_formatHeader.blockAlign);
                    writer.Write(_formatHeader.bitsPerSample);
                }
                else
                {
                    Console.WriteLine("No Format Header to write. Aborting");
                    return;
                }

                //Write Data Header              
                if (_dataHeader != null)
                {
                    writer.Write(_dataHeader.dataChunkID);
                    writer.Write(_dataHeader.dataChunkSize);
                }
                else
                {
                    Console.WriteLine("No Data Header to write. Aborting");
                    return;
                }

                switch (_formatHeader.bitsPerSample)
                {
                    case 16:
                        {
                            for (int i = 0; i < numberOfSamples; i++)
                            {
                                writer.Write(ConversionHelper.FloatToSixteenBit(_sampleData[i]));
                            }
                        } break;
                    case 24:
                        {
                            for (int i = 0; i < numberOfSamples; i++)
                            {
                                writer.Write(ConversionHelper.FloatToTwentyFourBit(_sampleData[i]));
                            }
                        } break;
                    case 32:
                        {
                            for (int i = 0; i < numberOfSamples; i++)
                            {
                                writer.Write(ConversionHelper.FloatToThirtyTwoBit(_sampleData[i]));
                            }
                        } break;
                    default:
                        {
                            Console.WriteLine("Invalid Bit Depth");
                            return;
                        }
                }
            }
        }

        public float GetSample(long samplePosition)
        {
            if (samplePosition < numberOfSamples && samplePosition >= 0)
                return _sampleData[samplePosition];
            else
                return 0.0f;
        }

        public void SetSample(long samplePosition, float sample)
        {
            _sampleData[samplePosition] = sample;
        }

        public int GetSampleRate()
        {
            return _formatHeader.sampleRate;
        }

        public short GetNumberOfChannels()
        {
            return _formatHeader.numberOfChannels;
        }

    }
}
